package telhai.java.gthreads;

import java.util.*;				// For vectors and exceptions
import java.awt.*;				// For graphical functions
import java.awt.event.*;		// For event handler
import javax.swing.*;			// For Swing GUI
import javax.swing.border.*;	// For Swing border functions
import java.io.*;				// For I/O functions

/** This class provides a dynamic interface for monitoring threads and objects. Once started, the GraphicalThreadManager
  * (GTM) utilizes a Swing timer to create timed events. In theory, the events should come at regular intervals of 50ms.
  * In practice, this doesn't always work out that way, and delays can be longer, depending on the system load.
  *
  * The GTM contains a single subcomponent of class TimeLine, which shows the time as a horizontal advancing line. On each
  * timer event (tick) the timeline advances by one unit (between 1 and 10 pixels).
  *
  * For each thread which is added to the GTM, a ThreadLine object is created, which shows the thread lifeline versus the
  * time. Like the time line, the thread line also advances on each tick, but only if the thread is alive. Different
  * colors are used to distinguish whether the thread is awake or sleeping. To allow this, the GTM does not work with
  * general Thread objects, but only with ThreadPlus objects.
  *
  * Each ThreadLine object also contains a text area which shows messages sent by the thread. Since each thread is a
  * ThreadPlus object, it contains a designated pipe output. The ThreadLine connects a pipe input to the output of the
  * thread and reads messages from it. Note that any output generated by the thread which was not sent to the designated
  * pipe will not be displayed in the text area.
  *
  * For each object added to the GTM, an ObjectViewTable is created. This table monitors all the fields of this object.
  * On each tick, the table is updated, showing any changes to the object's fields.
  *
  * It is possible to add and remove thread and objects at run-time. However, there is a limit to the number of threads
  * and objects which can be monitored at once. This is done for two reasons: to allow all threads and objects to be
  * displayed in one window, and to limit the system load, since the object requires a complete repaint on each timer
  * tick.
  *
  * @see GraphicalThreadManager.TimeLine
  * @see GraphicalThreadManager.ThreadLine
  * @see ObjectViewTable
  * @see ThreadPlus
  * 
  * @author Alex Frid alex.frid@gmail.com; Dima Ruinski
  */
public class GraphicalThreadManager extends JFrame implements ActionListener
{
	/** Maximum number of threads which can be monitored at the same time */
	public static final int MAX_THREADS = 5;

	/** Maximum number of objects which can be monitored at the same time */
	public static final int MAX_OBJECTS = 5;

	/** Minimum number of pixels per Timer event (the speed at which the timeline advances) */
	public static final int MIN_PPTICK = 1;

	/** Maximum number of pixels per Timer event (the speed at which the timeline advances) */
	public static final int MAX_PPTICK = 10;

	/** The base color for the text and timeline axis */
	public static final Color BASE_COLOR = Color.BLACK;

	/** The color for the components' borders */
	public static final Color BORDER_COLOR = Color.BLACK;

	/** Default color for the timeline */
	public static final Color DEFAULT_TIMELINE_COLOR = Color.RED;

	/** Default color for the active thread line */
	public static final Color DEFAULT_THREADLINE_COLOR = Color.GREEN.darker();

	/** Default color for the sleeping thread line */
	public static final Color DEFAULT_THREADSLEEP_COLOR = Color.MAGENTA.darker();

	/** Default width of the GTM window */
	private static final int DEFAULT_X_SIZE = 1000;

	/** Default height of the GTM window */
	private static final int DEFAULT_Y_SIZE = 750;
	
	/** The left border of everything which is drawn in the window */
	private static final int X_ORIGIN = 10;

	/** The top border of everything which is drawn in the window */
	private static final int Y_ORIGIN = 10;

	/** The space between two ThreadLine components */
	private static final int Y_SPACE = 20;

	/** The top border of the legend window */
	private static final int Y_LEGEND = 600;

	/** The X coordinate of the timeline starting point */
	private static final int X_BEGIN = 10;

	/** The X coordinate of the timeline ending point */
	private static final int X_END = 510;
	
	/** The X coordinate of the ObjectViewTable left border */
	private static final int X_OBJBEGIN = X_END+40;

	/** The X coordinate of the ObjectViewTable right border */
	private static final int X_OBJEND = DEFAULT_X_SIZE-10;

	/** The height of the ObjectViewTable */
	private static final int Y_OBJHEIGHT = (DEFAULT_Y_SIZE-50)/5;
	
	/** Current timeline color. By default set to DEFAULT_TIMELINE_COLOR, but can be changed.
	  * @see #setColors(Color,Color,Color)
	  * @see #getTimelineColor()
	  */
	private Color timelineColor;

	/** Current active thread color. By default set to DEFAULT_THREADLINE_COLOR, but can be changed.
	  * @see #setColors(Color,Color,Color)
	  * @see #getThreadlineColor()
	  */
	private Color threadlineColor;

	/** Current timeline color. By default set to DEFAULT_THREADSLEEP_COLOR, but can be changed.
	  * @see #setColors(Color,Color,Color)
	  * @see #getThreadsleepColor()
	  */
	private Color threadsleepColor;

	/** Internal position of the timer - determines the end point of the timeline and threadline painting.
	  * The timer position is incremented by a value between 1 and 10 on each timer event, up to a certain maximum,
	  * which is X_END-X_BEGIN. If automatic reset is enabled, the timer position will be reset to 0 and events will
	  * keep on coming. If it is disabled, the timer will stop.
	  * @see #setPixelsPerTick(int)
	  * @see #setAutoReset(boolean)
	  */
	private int timerPos;

	/** The value which is added to the internal timer position upon each event of the Swing Timer.
	  * Must be between 1 and 10. Literally, it means the number of pixels which will be added to the timelines on each
	  * Timer event.
	  * @see #setPixelsPerTick(int)
	  */
	private int pixelsPerTick;

	/** A flag which determines whether the internal timer should reset itself when it reaches the maximum.
	  * @see #setAutoReset(boolean)
	  */
	private boolean autoReset;

	/** Counts the number of milliseconds since the GTM was started or since resetInitTime() was last called.
	  * @see #resetInitTime()
	  */
	private long initTimeMillis;

	/** The Timer object which creates events for the GTM. Whenever a timer event is dispatched, the GTM checks the
	  * status of all its threads and objects, and repaints itself.
	  */
	private javax.swing.Timer mainTimer;
	
	/** The component which draws the main timeline of the GTM.
	  * @see GraphicalThreadManager.TimeLine
	  */
	private TimeLine generalTimeLine;
	
	/** An array of ThreadLine components.
	  * @see GraphicalThreadManager.ThreadLine
	  */
	private ThreadLine[] myThreads;
	
	/** An array of components which monitors objects and their fields.
	  * @see ObjectViewTable
	  */
	private ObjectViewTable[] myObjects;
	
	/** The legend of the GTM. Displays the colors and their meanings and also the current value of initTimeMillis.
	  * @see #initTimeMillis
	  * @see LegendPanel
	  */
	private LegendPanel legend;
	
	/**
	  * The number of threads currently monitored by the GTM.
	  * @see #addThread(ThreadPlus)
	  * @see #removeThread(int)
	  */
	private int numThreads;
	
	/**
	  * The number of objects currently monitored by the GTM.
	  * @see #addObject(Object)
	  * @see #removeObject(int)
	  */
	private int numObjects;

	/** This inner class represents the timeline of the GraphicalThreadManager. Each instance of the GTM has one
	  * TimeLine object which draws itself according to the internal timer (timerPos field). All active ThreadLine objects
	  * of the GTM synchronize to this TimeLine object and draw their time lines accordingly.
	  * @see GraphicalThreadManager#timerPos
	  * @see GraphicalThreadManager.ThreadLine
	  */
	private class TimeLine extends JPanel		// Inner class to implement a timeline
	{
		/** The Y coordinate of the time axis */
		private static final int Y_AXIS = 30;

		/** The Y coordinate for the advancing timeline */
		private static final int Y_TIMELINE = 45;

		/** The Y coordinate for the timeline text */
		private static final int Y_TEXT = 15;
		
		/** Constructs a time line component, sets it preferred size and border. */
		public TimeLine()
		{
			super();
			setPreferredSize(new Dimension(X_END+10,Y_TIMELINE+5));
			setBorder(new MatteBorder(1,1,1,1,BORDER_COLOR));
		}

		/** Paints the timeline. The painting involves several steps: first the component area is cleared. Then the axis
		  * is painted, using the base color. Then the timeline is painted, from the beginning to the current position of
		  * the internal timer.
		  */
		public void paintComponent(Graphics g)
		{
			int x,i;

			super.paintComponent(g);
			
			g.clearRect(0,0,getWidth(),getHeight());			// Clear component area
	
			g.setColor(BASE_COLOR);
			g.fillRect(X_BEGIN,Y_AXIS-1,X_END-X_BEGIN,3);		// Draw horizontal axis
			g.drawLine(X_BEGIN,Y_AXIS-10,X_BEGIN,Y_AXIS+10);	// Draw vertical start line
			g.drawLine(X_END,Y_AXIS-10,X_END,Y_AXIS+10);		// Draw vertical end line

			for (x=(X_BEGIN+(X_END-X_BEGIN)/10);x<X_END;x+=(X_END-X_BEGIN)/10)
				g.drawLine(x,Y_AXIS-5,x,Y_AXIS+5);		// Draw vertical lines along the axis

			g.drawString(("One unit: " + ((X_END-X_BEGIN)/(10*pixelsPerTick)) + " timer events"),X_BEGIN,Y_TEXT);

			g.setColor(timelineColor);
			g.fillRect(X_BEGIN,Y_TIMELINE,timerPos,2);			// Draw timeline
		}
	}

	/** This inner class represents a component which tracks a single ThreadPlus object. The ThreadLine is a rather
	  * complex component. It contains custom painting code to draw the lifeline of the thread and a JTextArea component
	  * to print the messages read from the thread's output pipe.
	  *
	  * Like the timeline, the thread's lifeline draws itself from the beginning to the current position of the GTM
	  * internal timer. However, there are a few differences: periods when the thread is active are painted using one
	  * color, and periods when it is asleep are painted using another color. Periods before the thread was started and
	  * after it had died are not painted at all.
	  *
	  * The JTextArea component is placed inside a JScrollPane to allow automatic scrolling. Each message read from the
	  * piped input corresponding to the thread's piped output will be written to the text area with the time when it
	  * arrived. The time is calculated as the difference between the current system time and the initTimeMillis field.
	  *
	  * @see ThreadPlus
	  * @see GraphicalThreadManager.TimeLine
	  * @see GraphicalThreadManager#initTimeMillis
	  * @see GraphicalThreadManager#threadlineColor
	  * @see GraphicalThreadManager#threadsleepColor
	  */
	private class ThreadLine extends JPanel		// Draws the lifeline of a single thread
	{
		/** The Y coordinate of the thread's name */
		private static final int Y_THREADNAME = 15;

		/** The Y coordinate of the thread's lifeline */
		private static final int Y_THREADLINE = 25;

		/** The Y coordinate of the JTextArea component */
		private static final int Y_TEXTBOX = 35;

		/** The height of the text area */
		private static final int Y_TEXTHEIGHT = 40;
		
		/** The thread which is monitored by this ThreadLine object */
		private ThreadPlus myThread;

		/** The reader to read from the thread's pipe */
		private Reader threadInput;

		/** The text area to print the thread's output */
		private JTextArea threadMessages;

		/** The scrollpane to contain the text area */
		private JScrollPane textBox;
				
		/** Holds the times where the thread changed from active to sleeping or vice-versa */
		private Vector stateChangeTimes;

		/** The time when the thread has ended (died) */
		private int endTime;

		/** Whether the thread was awake when this ThreadLine object received it */
		private boolean initialAwake;

		/** Whether the thread was awake on the last timer event */
		private boolean lastAwake;

		/** Whether the thread has already started */
		private boolean hasStarted;

		/** Whether the thread has already died */
		private boolean hasEnded;

		/** Constructs a ThreadLine component with no thread. This constructor is for compatibility only, and serves no
		  * real purpose, because there is no use for a ThreadLine component which doesn't monitor a thread.
		  */
		public ThreadLine()
		{
			super();
			setPreferredSize(new Dimension(X_END+10,Y_TEXTBOX+Y_TEXTHEIGHT+5));
			setBorder(new MatteBorder(1,1,1,1,BORDER_COLOR));

			myThread = null;
		}

		/** Constructs a ThreadLine component and sets it to monitor the given thread. A piped input stream is
		  * constructed and connected to the piped output stream of the thread. Also, the text area and the scrollpane
		  * are initialized.
		  * @param thr the thread to monitor.
		  */
		public ThreadLine (ThreadPlus thr)
		{
			super();
			setPreferredSize(new Dimension(X_END+10,Y_TEXTBOX+Y_TEXTHEIGHT+5));
			setBorder(new MatteBorder(1,1,1,1,BORDER_COLOR));

			myThread = thr;

			hasStarted = false;
			hasEnded = false;
			endTime = -1;						// Thread has not ended - has no endTime
			stateChangeTimes = new Vector();	// Vector to hold the thread's state change times.

			try		// Create an input readed and connect it to the thread's pipe
			{
				threadInput = new InputStreamReader(new PipedInputStream(myThread.getOutputStream()));
			}
			catch (IOException e) {e.printStackTrace();threadInput=null;}	// If failed, set the input stream to null

			threadMessages = new JTextArea();		// New text area
			threadMessages.setEditable(false);		// Text area is for output only, not for input

			textBox = new JScrollPane(threadMessages);
			textBox.setPreferredSize(new Dimension(X_END-X_BEGIN,Y_TEXTHEIGHT));
			add(textBox);						// Add the scrollpane as a child of this component
		}

		/** Notifies the ThreadLine object that a single timer event has occured. This method will check the state of
		  * the thread and compare it with the state of the thread during the last call to tick(). If a thread has changed
		  * from active to sleeping or vice-versa, the time of this event will be added to the vector of state changes.
		  */
		public void tick()
		{
			if (myThread==null)		// If no thread - do nothing
				return;

			if (!hasStarted)		// If thread hasn't started during the last call
			{
				if (myThread.isAlive())		// Check if it's alive
				{
					hasStarted = true;
					stateChangeTimes.add(new Integer(timerPos));	// Add thread start event to the state changes
					initialAwake = lastAwake = myThread.isAwake();	// See if the thread is currently awake
				}
			}
			else if (!hasEnded)				// If thread has started but hasn't ended during the last call
			{
				if (!myThread.isAlive())	// If the thread has died (ended)
				{
					hasEnded = true;
					endTime = timerPos;		// Set the end time to the current timing position
				}
				else						// If the thread is alive
				{
					boolean nowAwake = myThread.isAwake();	// See if it's awake

					if (nowAwake!=lastAwake)				// If state was changed between the last call and now
					{
						stateChangeTimes.add(new Integer(timerPos));	// Add to the state changes vector
						lastAwake = nowAwake;							// Update awake status
					}
				}
			}
		}

		/** Reset this ThreadLine object. The reset method clears the vector of state change times and initializes the
		  * hasStarted and hasEnded fields to false.
		  */
		public void reset()
		{
			stateChangeTimes.clear();
			hasStarted = hasEnded = false;
		}

		/** Internal method to change the drawing color for the thread's lifeline. If the old color was threadlineColor,
		  * threadsleepColor is returned. Otherwise, threadlineColor is returned.
		  * @param c the old color
		  * @return the new color
		  * @see GraphicalThreadManager#threadlineColor
		  * @see GraphicalThreadManager#threadsleepColor
		  */
		private Color changeColor (Color c)
		{
			if (c==threadlineColor)
				return threadsleepColor;
			return threadlineColor;
		}

		/** Internal method to retrieve the integer value of a given element at the state change times vector.
		  * @param i the index of the element (if index is out of bounds, -1 is returned)
		  * @return the integer value of the element at index i in the vector
		  */
		private int stateTime (int i)
		{
			if (i>=stateChangeTimes.size())
				return -1;
			return ((Integer)stateChangeTimes.elementAt(i)).intValue();
		}

		/** Paints the ThreadLine component. The painting involves several steps: first the component area is cleared.
		  * Then the thread's name is painted using the base color. Next, the thread's lifeline is painted. The lifeline
		  * starts at the timer position corresponding to the first event in the state changes vector (indicating the
		  * start of this thread). The initial color of the line depends on the value of the initialAwake flag, and it
		  * changes color at each position in the state changes vector. The line is painted until endTime if the thread
		  * has ended, or until the internal timer position of the GTM, if the thread has not yet ended. Finally, the
		  * method attempts to read from the thread's pipe. If there is input, it will be printed into the text area,
		  * with the current time stamp.
		  * @see #initialAwake
		  * @see #endTime
		  * @see #stateChangeTimes
		  */
		public void paintComponent(Graphics g)
		{
			int i,xLast;
			
			super.paintComponent(g);

			g.clearRect(0,0,getWidth(),getHeight());	// Clear component area

			if (myThread==null)			// If no thread - do nothing
				return;

			g.setColor(BASE_COLOR);
			g.drawString(myThread.getName(),X_BEGIN,Y_THREADNAME);	// Draw the thread's name

			if (!hasStarted)		// If thread hasn't started - nothing to draw
				return;
	
			if (hasEnded)			// If thread has ended
				xLast = endTime;	// set the ending point to it's end time
			else					// else
				xLast = timerPos;	// set the ending point to the current timer position

			if (initialAwake)					// Set the initial color of the thread's lifeline
				g.setColor(threadlineColor);
			else
				g.setColor(threadsleepColor);

			for (i=0;i<stateChangeTimes.size()-1;++i)	// Draw all parts of the thread's lifeline
			{
				g.fillRect(X_BEGIN+stateTime(i),Y_THREADLINE,stateTime(i+1)-stateTime(i),2);
				g.setColor(changeColor(g.getColor()));	// Change color whenever the thread has changed states
			}

			g.fillRect(X_BEGIN+stateTime(i),Y_THREADLINE,xLast-stateTime(i),2);	// Draw the last part of the lifeline

			if (threadInput==null)		// If the input stream has not been initalized, stop here
				return;

			try
			{
				StringBuffer s = new StringBuffer();

				while (threadInput.ready())
					s.append((char)(threadInput.read()));	// Read from the pipe, as long as there is something to read
				
				if (s.length()>0)		// If something was read, print it
					threadMessages.append((System.currentTimeMillis()-initTimeMillis) + " ms: " + s);
			}
			catch (IOException e) {e.printStackTrace();}
		}

		/** Clears the text box containing the output sent by the ThreadPlus object of this ThreadLine.
		  * This method is called whenever the GraphicalThreadManager undergoes a "hard reset".
		  * @see GraphicalThreadManager#resetHard()
		  */
		public void clearText()
		{
			threadMessages.setText("");
		}

		/** Validates this ThreadLine component, by ensuring that the textbox is properly position and sized.
		  */
		public void validate()
		{
			super.validate();
			textBox.setBounds(X_BEGIN,Y_TEXTBOX,textBox.getPreferredSize().width,textBox.getPreferredSize().height);
		}
	}

	/** This inner class represents the legend panel of the GraphicalThreadManager. The legend panel shows which colors
	  * are used for the timeline, the active thread line and the sleeping thread line at any given moment, and also
	  * counts the time elapsed since the GTM was started or since resetInitTime() was last invoked.
	  * @see #setColors(Color,Color,Color)
	  * @see #resetInitTime()
	  */
	private class LegendPanel extends JPanel
	{
		/** The width of this component */
		private static final int X_LEGENDWIDTH = 200;

		/** The height of this component */
		private static final int Y_LEGENDHEIGHT = 100;
		
		/** The X coordinate of the text starting point */
		private static final int X_TEXTOFFSET = 10;

		/** The X coordinate of the line starting point */
		private static final int X_LINEOFFSET = 150;

		/** The length of the line */
		private static final int X_LINELENGTH = 30;

		/** The Y coordinate of the timeline */
		private static final int Y_TLINE = 20;

		/** The Y coordinate of the active thread line */
		private static final int Y_TACTIVE = 40;

		/** The Y coordinate of the sleeping thread line */
		private static final int Y_TSLEEP = 60;

		/** The Y coordinate of the "time elapsed" line */
		private static final int Y_TTIME = 85;

		/** Constructs a legend panel, sets its preferred size and border. */
		public LegendPanel()
		{
			super();
			setPreferredSize(new Dimension(X_LEGENDWIDTH,Y_LEGENDHEIGHT));
			setBorder(new MatteBorder(1,1,1,1,BORDER_COLOR));
		}

		/** Paints the legend panel component. The legend panel includes explanations of the different colors used in
		  * the GTM and their meanings. There are 3 colors which the user can change: the color of the timeline, the
		  * color of the active thread and the color of the sleeping thread. The legend panel also shows the elapsed time
		  * (the difference between the current system time and the initTimeMillis field of the GraphicalThreadManager.
		  */
		public void paintComponent (Graphics g)
		{
			super.paintComponent(g);
			
			g.clearRect(0,0,getWidth(),getHeight());

			Color oldC = g.getColor();					// Save current color of the graphical context

			long thisTime = (initTimeMillis==0) ? 0 : (System.currentTimeMillis()-initTimeMillis);

			g.setColor(BASE_COLOR);
			g.drawString("General Timeline:",X_TEXTOFFSET,Y_TLINE);
			g.drawString("Active Thread:",X_TEXTOFFSET,Y_TACTIVE);
			g.drawString("Sleeping Thread:",X_TEXTOFFSET,Y_TSLEEP);
			g.setColor(timelineColor);
			g.fillRect(X_LINEOFFSET,Y_TLINE-5,X_LINELENGTH,2);			// Draw timeline example
			g.setColor(threadlineColor);
			g.fillRect(X_LINEOFFSET,Y_TACTIVE-5,X_LINELENGTH,2);		// Draw active thread example
			g.setColor(threadsleepColor);
			g.fillRect(X_LINEOFFSET,Y_TSLEEP-5,X_LINELENGTH,2);			// Draw sleeping thread example
			g.setColor(BASE_COLOR);
			g.drawString(("Elapsed time:        " + thisTime + " ms")
						,X_TEXTOFFSET,Y_TTIME);							// Draw time elapsed
			
			g.setColor(oldC);							// Restore previous color of the graphical context
		}
	}

	/** Creates a GTM with default drawing colors and default pixelsPerTick value (1). */
	public GraphicalThreadManager ()
	{
		super();
		setSize(DEFAULT_X_SIZE,DEFAULT_Y_SIZE);
		setColors(DEFAULT_TIMELINE_COLOR,DEFAULT_THREADLINE_COLOR,DEFAULT_THREADSLEEP_COLOR);
		setPixelsPerTick(1);

		initialize();
	}

	/** Creates a GTM with default drawing colors and a given pixelsPerTickValue.
	  * @param pixels 1-10 the number of pixels to add to the timelines on each timer event.
	  */
	public GraphicalThreadManager (int pixels)
	{
		super();
		setSize(DEFAULT_X_SIZE,DEFAULT_Y_SIZE);
		setColors(DEFAULT_TIMELINE_COLOR,DEFAULT_THREADLINE_COLOR,DEFAULT_THREADSLEEP_COLOR);
		setPixelsPerTick(pixels);

		initialize();
	}
	
	/** Creates a GTM with given drawing colors and default pixelsPerTick value (1).
	  * @param timeCol the color of the time line.
	  * @param threadCol the color of the active thread line.
	  * @param sleepCol the color of the sleeping thread line.
	  */
	public GraphicalThreadManager (Color timeCol, Color threadCol, Color sleepCol)
	{
		super();
		setSize(DEFAULT_X_SIZE,DEFAULT_Y_SIZE);
		setColors(timeCol,threadCol,sleepCol);
		setPixelsPerTick(1);

		initialize();
	}

	/** Creates a GTM with given drawing colors and given pixelsPerTick value.
	  * @param timeCol the color of the time line.
	  * @param threadCol the color of the active thread line.
	  * @param sleepCol the color of the sleeping thread line.
	  * @param pixels 1-10 the number of pixels to add to the timelines on each timer event.
	  */
	public GraphicalThreadManager (Color timeCol, Color threadCol, Color sleepCol, int pixels)
	{
		super();
		setSize(DEFAULT_X_SIZE,DEFAULT_Y_SIZE);
		setColors(timeCol,threadCol,sleepCol);
		setPixelsPerTick(pixels);

		initialize();
	}

	/** Creates a GTM with default colors and default pixelsPerTick value (1) and adds a thread to it.
	  * @param initThread the thread which will be automatically added.
	  */
	public GraphicalThreadManager (ThreadPlus initThread)
	{
		super();
		setSize(DEFAULT_X_SIZE,DEFAULT_Y_SIZE);
		setColors(DEFAULT_TIMELINE_COLOR,DEFAULT_THREADLINE_COLOR,DEFAULT_THREADSLEEP_COLOR);
		setPixelsPerTick(1);

		initialize();
		addThread(initThread);
	}

	/** Creates a GTM with given drawing colors and default pixelsPerTick value (1) and adds a thread to it.
	  * @param timeCol the color of the time line.
	  * @param threadCol the color of the active thread line.
	  * @param sleepCol the color of the sleeping thread line.
	  * @param initThread the thread which will be automatically added.	  
	  */
	public GraphicalThreadManager (Color timeCol, Color threadCol, Color sleepCol, ThreadPlus initThread)
	{
		super();
		setSize(DEFAULT_X_SIZE,DEFAULT_Y_SIZE);
		setColors(timeCol,threadCol,sleepCol);
		setPixelsPerTick(1);

		initialize();
		addThread(initThread);
	}

	/** Creates a GTM with given drawing colors and given pixelsPerTick value and adds a thread to it.
	  * @param timeCol the color of the time line.
	  * @param threadCol the color of the active thread line.
	  * @param sleepCol the color of the sleeping thread line.
	  * @param pixels 1-10 the number of pixels to add to the timelines on each timer event.
	  * @param initThread the thread which will be automatically added.
	  */
	public GraphicalThreadManager (Color timeCol, Color threadCol, Color sleepCol, int pixels, ThreadPlus initThread)
	{
		super();
		setSize(DEFAULT_X_SIZE,DEFAULT_Y_SIZE);
		setColors(timeCol,threadCol,sleepCol);
		setPixelsPerTick(pixels);

		initialize();
		addThread(initThread);
	}

	/** Set the value of the pixelsPerTick field. This value must be between 1 and 10 and determines how many pixels
	  * will be added to the time lines on each Timer event.
	  * @param pixels 1-10 the number of pixels to add to the timelines on each timer event. Values greater than 10 will
	  * be set to 10, while values less than 1 will be set to 1.
	  * @see #getPixelsPerTick()
	  */
	public void setPixelsPerTick (int pixels)
	{
		if (pixels<=MIN_PPTICK)
			pixelsPerTick=MIN_PPTICK;
		else if (pixels>=MAX_PPTICK)
			pixelsPerTick=MAX_PPTICK;
		else
			pixelsPerTick=pixels;
	}

	/** Return the current value of the pixelsPerTick field.
	  * @see #setPixelsPerTick(int)
	  */
	public int getPixelsPerTick()
	{
		return pixelsPerTick;
	}

	/** Set the automatic reset flag. If automatic reset is on, the internal timer position (timerPos field) will be
	  * reset when it reaches the maximum, causing the time lines to be drawn from scratch. If automatic reset is off,
	  * the timer events will stop when the internal timer reaches the maximum.
	  * @see #getAutoReset()
	  */
	public void setAutoReset (boolean auto)
	{
		autoReset = auto;
	}

	/** Return the current value of the autoReset field.
	  * @see #setAutoReset(boolean)
	  */
	public boolean getAutoReset()
	{
		return autoReset;
	}

	/** Set the drawing colors for the time line, the active thread line and the sleeping thread line.
	  * @param timeCol the color of the time line.
	  * @param threadCol the color of the active thread line.
	  * @param sleepCol the color of the sleeping thread line.
	  * @see #getTimelineColor()
	  * @see #getThreadlineColor()
	  * @see #getThreadsleepColor()
	  */
	public void setColors (Color timeCol, Color threadCol, Color sleepCol)
	{
		timelineColor = timeCol;
		threadlineColor = threadCol;
		threadsleepColor = sleepCol;
	}

	/** Return the current time line color.
	  * @see #setColors(Color,Color,Color)
	  */
	public Color getTimelineColor()
	{
		return timelineColor;
	}

	/** Return the current active thread line color.
	  * @see #setColors(Color,Color,Color)
	  */
	public Color getThreadlineColor()
	{
		return threadlineColor;
	}

	/** Return the current sleeping thread line color.
	  * @see #setColors(Color,Color,Color)
	  */
	public Color getThreadsleepColor()
	{
		return threadsleepColor;
	}

	/** Return the number of threads currently monitored by this GTM instance.
	  */
	public int getNumOfThreads()
	{
		return numThreads;
	}

	/** Return the number of objects currently monitored by this GTM instance.
	  */
	public int getNumOfObjects()
	{
		return numObjects;
	}
		
	/** Adds a thread (instance of ThreadPlus) to the threads monitored by this GTM instance.
	  * Each new thread will receive its own instance of ThreadLine which will track its lifeline and display any messages
	  * sent to the thread's dedicated pipe output.
	  * @param newThread the thread to add.
	  * @throws AddLimitReachedException when the GTM already monitors the maximum possible number of threads.
	  */
	public void addThread (ThreadPlus newThread) throws AddLimitReachedException
	{
		if (numThreads==MAX_THREADS)
			throw new AddLimitReachedException("Cannot add thread " + newThread.getName());

		myThreads[numThreads] = new ThreadLine(newThread);	// Add a ThreadLine component for this thread
		getContentPane().add(myThreads[numThreads]);		// Add the ThreadLine to the frame
		++numThreads;

		validate();					// Revalidate window
	}

	/** Removes a thread from the GTM, given its index.
	  * @param index the index of the thread to remove.
	  * @throws IndexOutOfBoundsException when the index indicates a thread that doesn't exist.
	  */
	public void removeThread (int index) throws IndexOutOfBoundsException
	{
		if (index>=numThreads)
			throw new IndexOutOfBoundsException("Cannot remove - index " + index + " out of bounds");

		--numThreads;
		
		getContentPane().remove(myThreads[index]);	// Remove the ThreadLine from the frame

		try
		{
			myThreads[index].threadInput.close();	// Close the input stream associated with this thread's output
		}
		catch (IOException e) {e.printStackTrace();}

		int i;
		for (i=index;i<numThreads;++i)				// Move the other threads in the array
			myThreads[i] = myThreads[i+1];
		myThreads[i] = null;
		
		repaint();							// Repaint the window
		validate();
	}

	/** Removes a thread from the GTM, given a reference to it.
	  * @param thr the thread to remove.
	  * @throws NoSuchElementException when the thread referenced by the parameter thr is not found in the GTM.
	  */
	public void removeThread (ThreadPlus thr) throws NoSuchElementException
	{
		int i;

		for (i=0;i<numThreads;++i)
			if (myThreads[i].myThread==thr)		// If thread found in the GTM
			{
				removeThread(i);				// Remove it by index
				return;
			}

		throw new NoSuchElementException("Cannot remove - no such thread in manager: " + thr.toString());
	}

	/** Removes all threads from the GTM.
	  */
	public void removeAllThreads()
	{
		while (numThreads>0)
			removeThread(0);
	}

	/** Adds an object (instance of Object) to the objects monitored by this GTM instance.
	  * Each new object will receive its own instance of ObjectViewTable which will display its fields and their values
	  * at any given time. This method actually calls to addObject(newObj,false).
	  * @param newObj the object to add.
	  */
	public void addObject (Object newObj)
	{
		addObject(newObj,false);
	}

	/** Adds an object (instance of Object) to the objects monitored by this GTM instance.
	  * Each new object will receive its own instance of ObjectViewTable which will display its fields and their values
	  * at any given time.
	  * @param newObj the object to add.
	  * @param ancestors indicates whether the inherited fields of this object should also be displayed.
	  * @throws AddLimitReachedException when the GTM already monitors the maximum possible number of objects.
	  */
	public void addObject (Object newObj, boolean ancestors) throws AddLimitReachedException
	{
		if (numObjects==MAX_OBJECTS)
			throw new AddLimitReachedException("Cannot add object " + newObj.toString());

		myObjects[numObjects] = new ObjectViewTable(newObj,new Dimension(X_OBJEND-X_OBJBEGIN,Y_OBJHEIGHT),ancestors);
		getContentPane().add(myObjects[numObjects]);		// Create new ObjectViewTable and add it to the frame
		++numObjects;

		validate();					// Revalidate frame
	}

	/** Removes an object from the GTM, given its index.
	  * @param index the index of the object to remove.
	  * @throws IndexOutOfBoundsException when the index indicates an object that doesn't exist.
	  */
	public void removeObject (int index) throws IndexOutOfBoundsException
	{
		if (index>=numObjects)
			throw new IndexOutOfBoundsException("Cannot remove - index " + index + " out of bounds");

		--numObjects;
		
		getContentPane().remove(myObjects[index]);		// Remove the ObjectViewTable from the frame

		int i;
		for (i=index;i<numObjects;++i)
			myObjects[i] = myObjects[i+1];		// Move the other objects
		myObjects[i] = null;
		
		repaint();					// Repaint the window
		validate();
	}

	/** Removes an object from the GTM, given a reference to it.
	  * @param obj the object to remove.
	  * @throws NoSuchElementException when the object referenced by the parameter obj is not found in the GTM.
	  */
	public void removeObject (Object obj) throws NoSuchElementException
	{
		int i;

		for (i=0;i<numObjects;++i)
			if (myObjects[i].myObject==obj)			// If object found in the GTM
			{
				removeObject(i);					// Remove it
				return;
			}

		throw new NoSuchElementException("Cannot remove - no such object in manager: " + obj.toString());
	}

	/** Removes all objects from the GTM.
	  */
	public void removeAllObjects()
	{
		while (numObjects>0)
			removeObject(0);
	}

	/** Starts this GraphicalThreadManager. This function invokes mainTimer.start() to cause the Swing Timer to start
	  * generating events. If the initTimeMillis field has not been initialized yet, it is initialized with the current
	  * system time.
	  */
	public void start()
	{
		if (initTimeMillis==0)
			initTimeMillis = System.currentTimeMillis();	// Initialize initTime if needed
		mainTimer.start();									// Start timer
	}

	/** Stops this GraphicalThreadManager. This function invokes mainTimer.stop() to cause the Swing Timer to stop
	  * generating events.
	  */
	public void stop()
	{
		mainTimer.stop();
	}

	/** Resets this GraphicalThreadManager. First stop() is called to stop the timer. Then the internal timer position
	  * is reset to 0. Finally, all the ThreadLine components are reset, causing all timelines to be reset.
	  * @see #stop()
	  * @see #resetHard()
	  */
	public void reset()
	{
		stop();						// Stop timer
		timerPos = 0;				// Reset internal timeline position

		for (int i=0;i<numThreads;++i)
			myThreads[i].reset();		// Reset thread lines

		repaint();
	}

	/** Performs a hard reset of this GraphicalThreadManager. Hard reset includes, resetting the timelines and also
	  * resetting the message boxes of all the ThreadLine objects and the init time. The function sets the initTimeMillis
	  * field to 0, so that it will be re-initialized on the next call to start(). Then it calls reset(), then
	  * ThreadLine.clearText() for all ThreadLine objects.
	  * @see #reset()
	  * @see GraphicalThreadManager.ThreadLine#clearText()
	  * @see #initTimeMillis
	  */
	public void resetHard()
	{
		initTimeMillis = 0;
		reset();

		for (int i=0;i<numThreads;++i)
			myThreads[i].clearText();		// Clear text of threads
	}

	/** Restarts this GraphicalThreadManager. This function basically invokes reset(), then start().
	  * @see #reset()
	  * @see #start()
	  */
	public void restart()
	{
		reset();
		start();
	}

	/** Performs a hard restart of this GraphicalThreadManager. The function basically invokes resetHard(), then start().
	  * @see #resetHard()
	  * @see #start()
	  */
	public void restartHard()
	{
		resetHard();
		start();
	}

	/** Resets the initialization time of this GraphicalThreadManager. This function sets initTimeMillis to the current
	  * system time.
	  */
	public void resetInitTime()
	{
		initTimeMillis = System.currentTimeMillis();
	}

	/** Invoked whenever an action event is sent to the GTM. Normally only the Swing Timer sends those events, and any
	  * events which are not from the timer will be ignored. Whenever an event from the timer is received, the GTM
	  * updates all of its components - the main timeline and all the thread lines advance by one or more pixels, and the
	  * tables displaying the objects fields are updated. All the components are repainted and the window title is
	  * updated as well.
	  */
	public void actionPerformed(ActionEvent e)
	{
		if (e.getSource()==mainTimer)						// Make sure that it's a timer event
		{
			if ((timerPos+=pixelsPerTick)>(X_END-X_BEGIN))	// Advance internal timer position by pixelsPerTick
			{
				if (autoReset)	// If the maximum is reached and auto-reset it on, restart the timer
					restart();
				else			// Else stop the timer
					stop();
			}
			generalTimeLine.repaint();			// Repaint timeline
			
			for (int i=0;i<numThreads;++i)
			{
				myThreads[i].tick();			// Advance all thread lines
				myThreads[i].repaint();			// Repaint thread lines
			}

			for (int i=0;i<numObjects;++i)
			{
				myObjects[i].repaint();			// Repaint object tables
			}

			legend.repaint();					// Repaint legend component
		}

		setGTMTitle();			// Set title to display the current number of threads and objects
	}

	/** Validates this frame to ensure that all components are drawn at the proper places with the proper sizes.
	  * This method will invoke the validate() methods of the GTM's subcomponents to ensure that they are repainted
	  * internally as should.
	  */
	public void validate()
	{
		super.validate();															// Validate JFrame
		
		generalTimeLine.setBounds(X_ORIGIN,Y_ORIGIN,							// Set bounds for the timeline
									generalTimeLine.getPreferredSize().width,
									generalTimeLine.getPreferredSize().height);

		int i,y;

		y = Y_ORIGIN + generalTimeLine.getPreferredSize().height + Y_SPACE;

		for (i=0;i<numThreads;++i)												// Set bounds for all the thread lines
		{
			myThreads[i].setBounds(X_ORIGIN,y,
									myThreads[i].getPreferredSize().width,
									myThreads[i].getPreferredSize().height);
			y = y + myThreads[i].getPreferredSize().height + Y_SPACE;

			myThreads[i].validate();		// Validate all thread lines
		}

		y = Y_ORIGIN;

		for (i=0;i<numObjects;++i)												// Set bounds for all the object tables
		{
			myObjects[i].setBounds(X_OBJBEGIN,y,
									myObjects[i].getPreferredSize().width,
									myObjects[i].getPreferredSize().height);
			y = y + Y_OBJHEIGHT+1;

			myObjects[i].validate();		// Validate all object tables
		}

		legend.setBounds(X_ORIGIN,Y_LEGEND,legend.getPreferredSize().width,legend.getPreferredSize().height);
	}

	/** Returns a string representation of this GraphicalThreadManager. The string representation includes the number of
	  * threads and objects currently monitored.
	  */
	public String toString()
	{
		return ("Graphical Thread Manager -  Threads: " + numThreads + " , Objects: " + numObjects);
	}

	/** Called internally by the constructors to initialize the GTM frame. Initialization includes creating the general
	  * timeline and the legend components and the Swing Timer.
	  */
	protected void initialize()
	{
		autoReset = true;				// By default auto-reset is enabled
		initTimeMillis = 0;				// '0' means 'not initialized' - will be initialized by the start() method

		setGTMTitle();										// Set title
		setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);		// Set default close operation

		generalTimeLine = new TimeLine();					// Create the main timeline
		getContentPane().add(generalTimeLine);

		myThreads = new ThreadLine[MAX_THREADS];			// Allocate array for threads
		numThreads = 0;

		myObjects = new ObjectViewTable[MAX_OBJECTS];		// Allocate array for objects
		numObjects = 0;

		legend = new LegendPanel();							// Create the legend component
		getContentPane().add(legend);

		mainTimer = new javax.swing.Timer(50,this);			// Timer which will send events to this GTM

		setVisible(true);
		validate();				// Validate frame to ensure proper position of components
	}

	/** Sets the title of the GTM frame. The title includes the number of threads and objects currently monitored, and
	  * therefore can change over time. This method is invoked by the initialize() and by actionPerformed() methods
	  * of this class.
	  */
	protected void setGTMTitle()
	{
		setTitle ("Graphical Thread Manager [ Threads: " + numThreads + " ; Objects: " + numObjects + " ]");
	}
}
